package com.zbangmall.wechat;

import com.alibaba.fastjson.JSON;
import com.zbangmall.common.HttpClientUtils;
import com.zbangmall.common.PropertiesUtil;
import com.zbangmall.wechat.domain.AccessToken4UserInfo;
import com.zbangmall.wechat.domain.Code4AccessToken;
import com.zbangmall.wechat.domain.ErrorDomain;
import com.zbangmall.wechat.domain.WechatUserInfo;
import org.apache.http.client.utils.URIBuilder;

import javax.servlet.http.HttpServletRequest;
import java.net.URI;
import java.net.URLEncoder;


/**
 * 微信三方登陆工具类
 */
public class WechatLoginClient {

	//region ********************************字段和静态代块*********************************************

	/**
	 * 替换
	 */
	private static final String RPLC = "%s";

	/**
	 * pc端
	 */
	private final static String AUTH_URL_PC = "https://open.weixin.qq.com/connect/qrconnect?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_login&state=%s#wechat_redirect";

	/**
	 * 公众号
	 */
	private final static String AUTH_URL_OFFICIAL_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_userinfo&state=%s#wechat_redirect";


	/**
	 * 公众号授权回调地址
	 */
	private static String getOfficialAccRedirectUrl() {
		return PropertiesUtil.propsWechat.getProperty("wechat.official_acc.redirect_url");
	}

	/**
	 * 网页应用的回调地址
	 */
	private static String getPcRedirectUrl() {
		return PropertiesUtil.propsWechat.getProperty("wechat.pc.redirect_url");
	}

	/**
	 * 网页应用的密钥
	 */
	private static String getPcSecret() {
		return PropertiesUtil.propsWechat.getProperty("wechat.pc.secret");
	}

	/**
	 * 公众号的密钥
	 */
	private static String getOfficialAccSecret() {
		return PropertiesUtil.propsWechat.getProperty("wechat.official_acc.secret");
	}

	/**
	 * 网页应用的appid
	 */
	private static String getPcAppid() {
		return PropertiesUtil.propsWechat.getProperty("wechat.pc.appid");
	}

	/**
	 * 公众号的appid
	 */
	private static String getOfficialAccAppid() {
		return PropertiesUtil.propsWechat.getProperty("wechat.official_acc.appid");
	}

	/**
	 * 是否成功
	 */
	private static void isSuccess(ErrorDomain obj) throws Exception {
		if (obj.getErrcode() != null) {
			throw new RuntimeException(obj.getErrcode() + ">>" + obj.getErrmsg());
		}
	}

	/**
	 * 获取移动应用的appid
	 */
	public static String getAppid() {
		return PropertiesUtil.propsWechat.getProperty("wechat.app.appid");
	}

	/**
	 * 获取移动应用的密钥
	 */
	public static String getAppSecret() {
		return PropertiesUtil.propsWechat.getProperty("wechat.app.secret");
	}
	//endregion ********************************字段和静态代块*********************************************

	//region ******************************** 微信获取code的访问地址 *********************************************

	public static String getPcAuthUrl() throws Exception {
		return getPcAuthUrl(AUTH_URL_PC, getPcRedirectUrl(), "");
	}

	public static String getPcAuthUrl(String state) throws Exception {
		return getPcAuthUrl(AUTH_URL_PC, getPcRedirectUrl(), state);
	}

	public static String getPcAuthUrl(String redirect_url, String state) throws Exception {
		return getPcAuthUrl(AUTH_URL_PC, redirect_url, state);
	}

	/**
	 * 电脑端微信登录获取code的链接
	 *
	 * @param url          请求地址
	 * @param redirect_url 回调地址
	 * @param state        携带参数
	 */
	private static String getPcAuthUrl(String url, String redirect_url, String state) throws Exception {
		try {
			url = url.replaceFirst(RPLC, getPcAppid())
					.replaceFirst(RPLC, URLEncoder.encode(redirect_url, "UTF-8"))
					.replaceFirst(RPLC, URLEncoder.encode(state, "UTF-8"));
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
		return url;
	}

	public static String getOfficialAccAuthUrl() throws Exception {
		return getOfficialAccAuthUrl(AUTH_URL_OFFICIAL_URL, getOfficialAccRedirectUrl(), "");
	}

	public static String getOfficialAccAuthUrl(String state) throws Exception {
		return getOfficialAccAuthUrl(AUTH_URL_OFFICIAL_URL, getOfficialAccRedirectUrl(), state);
	}

	public static String getOfficialAccAuthUrl(String redirect_url, String state) throws Exception {
		return getOfficialAccAuthUrl(AUTH_URL_OFFICIAL_URL, redirect_url, state);
	}

	/**
	 * 公众号获取code的链接
	 *
	 * @param url          请求地址
	 * @param redirect_url 回调地址
	 * @param state        携带参数
	 */
	public static String getOfficialAccAuthUrl(String url, String redirect_url, String state) throws Exception {
		try {
			url = url.replaceFirst(RPLC, getOfficialAccAppid())
					.replaceFirst(RPLC, URLEncoder.encode(redirect_url, "UTF-8"))
					.replaceFirst(RPLC, URLEncoder.encode(state, "UTF-8"));
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
		return url;
	}

	//endregion ******************************** 微信获取code的访问地址 *********************************************


	//region ******************************** 通过code获取微信用户信息 *********************************************

	/**
	 * 这个方法的主要目的是，如果有业务需求要获取用户是，通过什么途径授权的。
	 * 例如：有一个加盟商要统计每个加盟店的用户关注情况，可以在授权地址中 state 存取值，在这里取到 state 的值
	 */
	public static Code4AccessToken getCode4AccessToken(HttpServletRequest request) throws Exception {
		return new Code4AccessToken(request.getParameter("code"), request.getParameter("state"));
	}

	//region ---------------- 【关心】 用户通过什么途径授权的 ----------------

	public static WechatUserInfo getUserInfoByCode4PC(Code4AccessToken code4AccessToken) throws Exception {
		AccessToken4UserInfo accessToken = getAccessToken(code4AccessToken, getPcAppid(), getPcSecret());
		return getUserInfo(accessToken);
	}

	public static WechatUserInfo getUserInfoByCode4App(Code4AccessToken code4AccessToken) throws Exception {
		AccessToken4UserInfo accessToken = getAccessToken(code4AccessToken, getAppid(), getAppSecret());
		return getUserInfo(accessToken);
	}

	public static WechatUserInfo getUserInfoByCode4OffcialAcc(Code4AccessToken code4AccessToken) throws Exception {
		AccessToken4UserInfo accessToken = getAccessToken(code4AccessToken, getOfficialAccAppid(), getOfficialAccSecret());
		return getUserInfo(accessToken);
	}
	//endregion ---------------- 【关心】 用户通过什么途径授权的 ----------------

	//region ---------------- 【不关心】 用户通过什么途径授权的 ----------------

	public static WechatUserInfo getUserInfoByCode4PC(HttpServletRequest request) throws Exception {
		AccessToken4UserInfo accessToken = getAccessToken(getCode4AccessToken(request), getPcAppid(), getPcSecret());
		return getUserInfo(accessToken);
	}

	public static WechatUserInfo getUserInfoByCode4App(HttpServletRequest request) throws Exception {
		AccessToken4UserInfo accessToken = getAccessToken(getCode4AccessToken(request), getAppid(), getAppSecret());
		return getUserInfo(accessToken);
	}

	public static WechatUserInfo getUserInfoByCode4OffcialAcc(HttpServletRequest request) throws Exception {
		AccessToken4UserInfo accessToken = getAccessToken(getCode4AccessToken(request), getOfficialAccAppid(), getOfficialAccSecret());
		return getUserInfo(accessToken);
	}
	//endregion ---------------- 【不关心】 用户通过什么途径授权的 ----------------

	//endregion ******************************** 通过code获取微信用户信息 *********************************************

	//region ******************************** 微信获取用户信息流程 *********************************************

	/**
	 * 【1】拿着微信给的 code 去问微信要 access_token 和当前用户的 openid
	 */
	private static AccessToken4UserInfo getAccessToken(Code4AccessToken code, String appid, String appSecret) throws Exception {
		URI url = new URIBuilder().setScheme("https")
				.setHost("api.weixin.qq.com")
				.setPath("/sns/oauth2/access_token")
				.setParameter("appid", appid)
				.setParameter("secret", appSecret)
				.setParameter("code", code.getCode())
				.setParameter("grant_type", "authorization_code")
				.build();
		String               json = HttpClientUtils.connectByGet(url);
		AccessToken4UserInfo obj  = JSON.parseObject(json, AccessToken4UserInfo.class);
		isSuccess(obj);
		return obj;
	}

	/**
	 * 【2】拿着 access_token 和 openid 去问微信要这个用户对应的资料
	 */
	private static WechatUserInfo getUserInfo(AccessToken4UserInfo token) throws Exception {
		URI url = new URIBuilder().setScheme("https")
				.setHost("api.weixin.qq.com")
				.setPath("/sns/userinfo")
				.setParameter("access_token", token.getAccess_token())
				.setParameter("openid", token.getOpenid())
				.build();

		String         json = HttpClientUtils.connectByGet(url);
		WechatUserInfo obj  = JSON.parseObject(json, WechatUserInfo.class);
		isSuccess(obj);
		return obj;
	}

	//endregion ******************************** 微信获取用户信息流程 *********************************************

}
